package model.editormodel;

import java.util.List;
import java.util.Observable;
import model.editormodel.ErrorMessage.ErrorType;

/**
 * <b>Erbt von:</b> <code>Observable</code> <br>
 * <b>Zugehoerige Observer:</b> <code>InfoArea</code> <br>
 * <b>In Verbindung stehende Klassen:</b> <code>EditorModel, CheckThread</code><br>
 * <b>Funktion:</b> Ausfuehrung der Checks: <br>
 * - alle Eingaenge verbunden? <br>
 * - alle Ausgaenge verbunden? <br>
 * - Test auf Rueckkopplung <br>
 * - usw. <br>
 * <br>
 * Wird von der Controllerklasse <code>CheckListener</code> bei gegebener
 * Benutzeraktion aufgerufen. Hier werden die Tests einer Schaltung auf Fehler
 * in einem eigenen Thread ausgefuehrt. Die Klasse wird von
 * <code>Infoarea</code> ueberwacht, welche die Fehler dem Benutzer ausgibt.
 * 
 * @author Team Locsi
 * 
 */
public class Check extends Observable {

	private EditorModel eModel;
	private CheckThread checkThread;

	/**
	 * Dieser Konstruktor erzeugt ein neues <code>Check</code>-Objekt.
	 * 
	 * @param eModel
	 *            <code>EditorModel</code> - Referenz auf das
	 *            <code>EditorModel</code>
	 */
	public Check(EditorModel eModel) {
		this.eModel = eModel;
	}

	/**
	 * Ein neues <code>CheckThread</code>-Objekt wird erzeugt. Ausserdem wird
	 * der CheckThread gestartet.
	 */
	public void startCheckThread() {
		checkThread = new CheckThread();
		checkThread.start();
	}

	/**
	 * Diese Methode fuehrt die einzelnen Checks aus und uebergibt mittels
	 * notifyObservers() anschliessend der InfoArea eine <code>Map</code> mit
	 * den Checkergebnissen und den zugehoerigen Elementen.
	 * 
	 * @throws InterruptedException
	 */
	private void doCheck() throws InterruptedException {
		boolean oneCheckFailed = false;
		CircuitModel cModel = this.eModel.getCircuitModel();
		List<LogicEditorElement> errorElements;
		List<Input> errorInputs;

		// pruefen ob es sich um eine leere Schaltung handelt
		if (this.eModel.isEmptyCircuit()) {
			oneCheckFailed = true;
			setChanged();
			notifyObservers(new ErrorMessage(ErrorType.EMPTY_CIRCUIT));
		} else {
			setChanged();
			notifyObservers(new ErrorMessage(ErrorType.NO_EMPTY_CIRCUIT));
		}

		// pruefen ob alle Eingaenge belegt sind
		errorInputs = cModel.getUnrelatedInputs();
		if (errorInputs.size() > 0) {
			oneCheckFailed = true;
			setChanged();
			notifyObservers(new ErrorMessage(ErrorType.MISSING_INPUT_CONNECTION));
		} else {
			setChanged();
			notifyObservers(new ErrorMessage(ErrorType.INPUT_CHECK_OKAY));
		}

		// pruefen ob es eventuell Teilnetze ohne Schalter, Clock oder Lampen
		// gibt. Ausserdem muss jedes Teilnetz noch mindestens einen weiteren
		// Elementtypen als die zuvor genannten beinhalten
		List<LogicEditorElement> sWcOL = this.eModel.getCircuitModel()
				.incorrectSubnets();
		if (sWcOL.size() > 0) {
			oneCheckFailed = true;
			setChanged();
			notifyObservers(new ErrorMessage(ErrorType.MISSING_SWITCH_CLOCK));
		} else {
			setChanged();
			notifyObservers(new ErrorMessage(ErrorType.SUBNET_CHECK_OKAY));
		}
		errorElements = sWcOL;

		// ist einer der Checks fehlgeschlagen so werden die fehlerttraechtigen
		// Komponenten dem Observer und dem Observer des EditorModels ubergeben
		if (oneCheckFailed) {
			this.eModel.sendIncorrectElements(new ErrorMessage(errorElements,
					errorInputs));
			setChanged();
			notifyObservers(new ErrorMessage(
					ErrorMessage.ErrorType.ONE_CHECK_FAILED));

			// sind alle Checks positiv abgelaufen so wird dem Observer und dem
			// Observer des Editormodels eine positive Nachricht mitgeteilt
		} else {
			this.eModel.sendIncorrectElements(new ErrorMessage(
					ErrorMessage.ErrorType.ALL_CHECKS_OK));
			setChanged();
			notifyObservers(new ErrorMessage(
					ErrorMessage.ErrorType.ALL_CHECKS_OK));
		}

	}

	/**
	 * <b>Erbt von:</b> <code>Thread</code> <br>
	 * Fuehrt die Checks der Schaltung auf Fehler als eigenen Thread durch.
	 * 
	 * @author Team Locsi
	 * 
	 */
	private class CheckThread extends Thread {

		/**
		 * Diese Methode fuehrt die <code>doCheck()</code>-Methode aus.
		 */
		@Override
		public void run() {
			try {
				doCheck();
				synchronized (eModel) {
					eModel.notifyAll();
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}
